/**
 * Package: Math
 * 
 * Requires:
 */

#define PI 3.14159265358979
#define E  2.71828182845904

/**************************************************************
  NUMBER THEORY
**************************************************************/
/// <summary>Returns the absolute value of a number.</summary>
/// <param name="a">The number to convert.</param>
/// <returns>The absolute value of the specified number.</returns>
int Math_Abs(int a)
{
    return (a < 0 ? -a : a);
}

/// <summary>Returns the absolute value of a number.</summary>
/// <param name="val">The number to convert.</param>
/// <returns>The absolute value of the specified number.</returns>
float Math_AbsFloat $opc(0x4A) (float val);

/// <summary>Returns the smaller of two numbers.</summary>
/// <param name="a">The first number.</param>
/// <param name="b">The second number.</param>
/// <returns>The smaller number.</returns>
int Math_Min(int a, int b)
{
    return (a < b ? a : b);
}

/// <summary>Returns the smaller of two numbers.</summary>
/// <param name="a">The first number.</param>
/// <param name="b">The second number.</param>
/// <returns>The smaller number.</returns>
float Math_MinFloat(float a, float b)
{
    return (a < b ? a : b);
}

/// <summary>Returns the larger of two specified numbers.</summary>
/// <param name="a">The first number.</param>
/// <param name="b">The second number.</param>
/// <returns>The larger number.</returns>
int Math_Max(int a, int b)
{
    return (a > b ? a : b);
}

/// <summary>Returns the larger of two specified numbers.</summary>
/// <param name="a">The first number.</param>
/// <param name="b">The second number.</param>
/// <returns>The larger number.</returns>
float Math_MaxFloat(float a, float b)
{
    return (a > b ? a : b);
}

/// <summary>Clamps a number to the specified interval.</summary>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <param name="value">The value to clamp.</param>
/// <returns>The clamped value.</returns>
int Math_Clamp(int min, int max, int value)
{
    return (value > max ? max : (value > min ? min : value));
}

/// <summary>Performs the signum function on a number.</summary>
/// <param name="val">The number to process.</param>
/// <returns>1 if val is greater than 0, 0 if val is 0, -1 if val is less than 0.</returns>
int Math_Sgn(int val)
{
    return (val > 0 ? 1 : (val < 0 ? -1 : 0));
}

/// <summary>Returns the largest number less than or equal to the specified number.</summary>
/// <param name="val">The specified number.</param>
/// <returns>The largest number less than or equal to the specified number.</returns>
float Math_Floor $opc(0x47) (float val);

/// <summary>Returns the largest number greater than or equal to the specified number.</summary>
/// <param name="val">The specified number.</param>
/// <returns>The smallest number less than or equal to the specified number.</returns>
float Math_Ceil $opc(0x48) (float val);

/// <summary>Returns the number nearest the specified value.</summary>
/// <param name="val">The specified value.</param>
/// <returns>The number nearest the specified value.</returns>
float Round $opc(0x81) (float val);

/**************************************************************
  POWER, LOGARITHM
**************************************************************/
/// <summary>Returns a specified number raised to the specified power.</summary>
/// <param name="base">The number to raise.</param>
/// <param name="exponent">The power to raise to.</param>
/// <returns>The specified number raised to the specified power.</returns>
float Math_Pow $opc(0x53) (float base, float exponent);

/// <summary>Returns e raised to the specified power.</summary>
/// <param name="val">The power to raise to.</param>
/// <returns>e raised to the specified power.</returns>
float Math_Exp $opc(0x54) (float val);

/// <summary>Raises a number to the power of 2.</summary>
/// <param name="a">The number to raise.</param>
/// <returns>The number raised to the power of 2.</returns>
float Math_Sqr(float a)
{
    return a * a;
}

/// <summary>Returns the square root of the specified number.</summary>
/// <param name="val">The specified number.</param>
/// <returns>The square root of the specified number.</returns>
float Math_Sqrt $opc(0x46) (float val);

/// <summary>Returns the number multiplied by 2 raised to the power of exponent.</summary>
/// <param name="val">The number to process.</param>
/// <param name="exponent">The exponent to use.</param>
/// <returns>The number multiplied by 2 raised to the power of exponent.</returns>
float Math_LDExp $opc(0x49) (float val, int exponent);

/// <summary>Returns the base x logarithm of a specified number.</summary>
/// <param name="base">The base to use.</param>
/// <param name="number">The specified number.</param>
/// <returns>The base x logarithm of a specified number.</returns>
float Math_Log(float base, float number)
{
    return Math_Ln(number) / Math_Ln(base);
}

/// <summary>Returns the base n logarithm of a specified number.</summary>
/// <param name="val">The specified number.</param>
/// <returns>The base n logarithm of a specified number.</returns>
float Math_Ln $opc(0x4B) (float val);

/// <summary>Returns the base 10 logarithm of a specified number.</summary>
/// <param name="val">The specified number.</param>
/// <returns>The base 10 logarithm of a specified number.</returns>
float Math_Log10 $opc(0x4C) (float val);

/**************************************************************
  GEOMETRY
**************************************************************/
/// <summary>Returns the distance between two points.</summary>
/// <param name="x1">The first coordinate of the first point.</param>
/// <param name="y1">The first coordinate of the second point.</param>
/// <param name="x2">The second coordinate of the first point</param>
/// <param name="y2">The second coordinate of the second point</param>
/// <returns>The distance between two points.</returns>
float Math_Distance(float x1, float y1, float x2, float y2)
{
    return sqrt(Sqr(x2 - x1) + Sqr(y2 - y1));
}

/// <summary>Returns the 3D distance between two points.</summary>
/// <param name="x1">The first coordinate of the first point.</param>
/// <param name="y1">The first coordinate of the second point.</param>
/// <param name="z1">The first coordinate of the third point.</param>
/// <param name="x2">The second coordinate of the first point</param>
/// <param name="y2">The second coordinate of the second point</param>
/// <param name="z2">The second coordinate of the third point.</param>
/// <returns>The 3D distance between two points.</returns>
float Math_Distance3D(float x1, float y1, float z1, float x2, float y2, float z2)
{
    return sqrt(Sqr(x2 - x1) + Sqr(y2 - y1) + Sqr(z2 - z1));
}

/**************************************************************
  TRIGONOMETRY
**************************************************************/
/// <summary>Returns the sine of the specified angle.</summary>
/// <param name="val">An angle, measured in radians.</param>
/// <returns>The sine of val.</returns>
float Math_Sin $opc(0x4D) (float val);

/// <summary>Returns the angle whose sine is the specified number.</summary>
/// <param name="val">A number representing a sine, where -1 = val = 1.</param>
/// <returns>An angle, measured in radians.</returns>
float Math_ASin $opc(0x4E) (float val);

/// <summary>Returns the cosine of the specified angle.</summary>
/// <param name="val">An angle, measured in radians. </param>
/// <returns>The cosine of val.</returns>
float Math_Cos $opc(0x4F) (float val);

/// <summary>Returns the angle whose cosine is the specified number.</summary>
/// <param name="val">A number representing a cosine, where -1 = val = 1. </param>
/// <returns>An angle, measured in radians.</returns>
float Math_ACos $opc(0x50) (float val);

/// <summary>Returns the tangent of the specified angle.</summary>
/// <param name="val">An angle, measured in radians. </param>
/// <returns>The tangent of val.</returns>
float Math_Tan $opc(0x51) (float val);

/// <summary>Returns the angle whose tangent is the specified number.</summary>
/// <param name="val">A number representing a tangent. </param>
/// <returns>An angle, measured in radians.</returns>
float Math_ATan $opc(0x52) (float val);

/// <summary>Returns the secant of the specified angle.</summary>
/// <param name="val">An angle, measured in radians.</param>
/// <returns>The secant of val.</returns>
float Math_Sec(float val)
{
    return 1.0 / Math_Cos(val);
}

// TODO: Math_ASec

/// <summary>Returns the cosecant of the specified angle.</summary>
/// <param name="val">An angle, measured in radians.</param>
/// <returns>The cosecant of val.</returns>
float Math_Csc(float val)
{
    return 1.0 / Math_Sin(val);
}

// TODO: Math_ACsc